package com.octopus.json;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * @Note
 * There three kinds of Json objects.
 * <ul>
 * <li>1) Object : the structure is just used to keep name and value, the inner implementation uses HashMap, that equals {} </li>
 * <li>2) Array: It represents an array, that equals []</li>
 * <li>3) SimpleValue: It represents the types of Integer, Long, Double, String</li>
 * </ul>
 *
 * @Note: Some of methods of Json instance only apply for a specific type of Json instances. 
 */
public class Json {
	
	private Map<String, Json> map;
	private List<Json> array;
	private int type;
	private Object value;
	
	private boolean mapJson;
	private boolean arrayJson;
	private boolean simpleValueJson;
	
	private Map<String, Object> reservedMap = new HashMap<String, Object>();
	
	public void cleanReservedMap(){
		reservedMap.clear();
	}
	
	public void putReservedMap(String key, Object object){
		this.reservedMap.put(key, object);
	}
	
	public Object getReservedMap(String key){
		return this.reservedMap.get(key);
	}
	
	public Object removeReservedMap(String key){
		return this.reservedMap.remove(key);
	}
	
	Json(){
	}
	
	/**
	 * @Note 
	 * <br/>Initialize Json Object instance.
	 * <br/>The map is used to store name-value pairs. 
	 */
	void initObject(){
		this.mapJson = true;
		this.map = new HashMap<String, Json>();
	}
	
	/**
	 * @Note <br/>Initialize Json array Instance.
	 * <br/>The List is used to store Json instances in array manner. 
	 */
	void initArray(){
		this.arrayJson = true;
		this.array = new ArrayList<Json>();
	}
	
	/**
	 * @Note <br/>Initialize Json simpleVlaue instance with Integer.
	 */
	void initSimpleValue(Integer value){
		this.simpleValueJson = true;
		setValue(value);
	}
	
	/**
	 * @Note <br/>Initialize Json simpleVlaue instance with Long.
	 */
	void initSimpleValue(Long value){
		this.simpleValueJson = true;
		setValue(value);
	}
	
	/**
	 * @Note <br/>Initialize Json simpleVlaue instance with Double.
	 */
	void initSimpleValue(Double value){
		this.simpleValueJson = true;
		setValue(value);
	}
	
	/**
	 * @Note <br/>Initialize Json simpleVlaue instance with String.
	 */
	void initSimpleValue(String value){
		this.simpleValueJson = true;
		setValue(value);
	}
	
	/**
	 * @Note 
	 * 	Check whehter the json instance represents Integer.
	 * @applyFor All
	 */
	public boolean isInt(){
		return this.simpleValueJson && this.type == JsonConstants.TYPE_INT;
	}
	/**
	 * @Note 
	 *  Check whether the json instace represents Long
	 * @applyFor All
	 */
	public boolean isLong(){
		return this.simpleValueJson && this.type == JsonConstants.TYPE_LONG;
	}
	/**
	 * @Removed
	 * @applyFor All
	 */
	/*public boolean isFloat(){
		return this.simpleValueJson && this.type == JsonConstants.TYPE_FLOAT;
	}*/
	
	/**
	 * @Note Check whehter the json instance represents Double
	 * @applyFor All
	 */
	public boolean isDouble(){
		return this.simpleValueJson && this.type == JsonConstants.TYPE_DOUBLE;
	}
	/*
	 * @applyFor All
	 */
	/*public boolean isBigDecimal(){
		return this.simpleValueJson && this.type == JsonConstants.TYPE_BIG_DECIMAL;
	}*/
	
	/**
	 * @Note Check whether the json instance represents String
	 * @applyFor All
	 */
	public boolean isString(){
		return this.simpleValueJson && this.type == JsonConstants.TYPE_STRING;
	}
	
	/**
	 * @Note Check whether the json instance represents Object - {}
	 * @applyFor All
	 */
	public boolean isObject(){
		return this.mapJson;
	}
	
	/**
	 * @Note Check whehter the json instance represents Array - []
	 * @applyFor All
	 */
	public boolean isArray(){
		return this.arrayJson;
	}
	
	/**
	 * @Note Check whether the json instance represents simple value, for instance: Integer, Long, Double, String.
	 * @applyFor All
	 */
	public boolean isSimpleValueJson(){
		return this.simpleValueJson;
	}
	
	/**
	 * @return The length of json instance which represents Array. If not an Array, return 0. 
	 * @applyFor all
	 */
	public int length(){
		if(this.isArray()){
			return this.array.size();
		}else{
			return 0;
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note The value must be one instance of these:Integer, Long, Double, String. Any other object will make an effect that an UnsupportedOperationException will be thrown out.
	 * <br/> Non-SimpleValue Json instance call this method, UnsupportedOperationException will be thrown out.
	 * <br/> Non-(Integer, Long, Double, String) includes null is used, UnsupportedDataTypeException will be thrown out.
	 * @applyFor SimpleValue
	 */
	/*public void setValue(Object value){
		if(this.isSimpleValueJson()){
			if(value == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			if(value instanceof Integer){
				this.type = JsonConstants.TYPE_INT;
			}else if(value instanceof Long){
				this.type = JsonConstants.TYPE_LONG;
			}else if(value instanceof Float){
				this.type = JsonConstants.TYPE_FLOAT;
			}else if(value instanceof Double){
				this.type = JsonConstants.TYPE_DOUBLE;
			}else if(value instanceof BigDecimal){
				this.type = JsonConstants.TYPE_BIG_DECIMAL;
			}else if(value instanceof String){
				this.type = JsonConstants.TYPE_STRING;
			}else{
				//throw new UnsupportedDataTypeException(StringUtils.concat(value.getClass().getName(), " is unsupported type, json only supports Integer, Long, Float, Double, BigDecimal, String"));
				throw new UnsupportedDataTypeException(StringUtils.concat(value.getClass().getName(), " is unsupported type, json only supports Integer, Long, Double, String"));
			}
			this.value = value;
		}else{
			throw new UnsupportedOperationException("non simple value json doesn't support setValue method");
		}
	}*/
	
	
	/**
	 * @param Integer
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Don't support null value. If value is null, UnsupportedDataTypeException will be thrown.
	 * @applyFor SimpleValue
	 */
	public void setValue(Integer value){
		if(this.isSimpleValueJson()){
			if(value == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			this.type = JsonConstants.TYPE_INT;
			this.value = value;
		}else{
			throw new UnsupportedOperationException("non simple value json doesn't support setValue integer method");
		}
	}
	
	/**
	 * @param Long
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Don't support null value. If value is null, UnsupportedDataTypeException will be thrown.
	 * @applyFor SimpleValue
	 */
	public void setValue(Long value){
		if(this.isSimpleValueJson()){
			if(value == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			this.type = JsonConstants.TYPE_LONG;
			this.value = value;
		}else{
			throw new UnsupportedOperationException("non simple value json doesn't support setValue long method");
		}
	}
	
	/**
	 * @param Double
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Don't support null value. If value is null, UnsupportedDataTypeException will be thrown.
	 * @applyFor SimpleValue
	 */
	public void setValue(Double value){
		if(this.isSimpleValueJson()){
			if(value == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			this.type = JsonConstants.TYPE_DOUBLE;
			this.value = value;
		}else{
			throw new UnsupportedOperationException("non simple value json doesn't support setValue double method");
		}
	}
	
	/**
	 * @param String
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Don't support null value. If value is null, UnsupportedDataTypeException will be thrown.
	 * @applyFor SimpleValue
	 */
	public void setValue(String value){
		if(this.isSimpleValueJson()){
			if(value == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			this.type = JsonConstants.TYPE_STRING;
			this.value = value;
		}else{
			throw new UnsupportedOperationException("non simple value json doesn't support setValue string method");
		}
	}
	
	/**
	 * @throws UnsupportedOperationException
	 * @Note If Non-SimpleValue Json instance call this method, UnsupportedOperationException will be thrown out.
	 * @applyFor SimpleValue
	 */
	public Object getValue(){
		if(this.isSimpleValueJson()){
			return this.value;
		}else{
			throw new UnsupportedOperationException("non simple value json doesn't support setValue method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of Integer
	 * @applyFor Object
	 */
	public void keyValue(String key, Integer value){
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			this.map.put(key, json);
		}else{
			throw new UnsupportedOperationException("non object json doesn't support keyValue method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of Long
	 * @applyFor Object
	 */
	public void keyValue(String key, Long value){
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			this.map.put(key, json);
		}else{
			throw new UnsupportedOperationException("non object json doesn't support keyValue method");
		}
	}
	
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of Double
	 * @applyFor Object
	 */
	public void keyValue(String key, Double value){
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			this.map.put(key, json);
		}else{
			throw new UnsupportedOperationException("non object json doesn't support keyValue method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of String
	 * @applyFor Object
	 */
	public void keyValue(String key, String value){
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			this.map.put(key, json);
		}else{
			throw new UnsupportedOperationException("non object json doesn't support keyValue method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> If value is null, do nothing.
	 * @applyFor Object
	 */
	public void keyJsonValue(String key, Json value){
		if(value == null){
			return;
		}
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			this.map.put(key, value);
		}else{
			throw new UnsupportedOperationException("non object json doesn't support keyValue json method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of Integer
	 * <br/> If the key property doesn't exist in the inner map, just put them.
	 * <br/> If the key property already exists in the inner map, if the exist one is an Array Json, just append it to the end of it, If not an Array Json, use new Array Json to replace it and append both current and new to the end of the Array Json   
	 * @applyFor Object
	 */
	public void appendKeyValue(String key, Integer value){
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			appendKeyJsonValue(key, json);
		}else{
			throw new UnsupportedOperationException("non object json doesn't support appendKeyValue method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of Long
	 * <br/> If the key property doesn't exist in the inner map, just put them.
	 * <br/> If the key property already exists in the inner map, if the exist one is an Array Json, just append it to the end of it, If not an Array Json, use new Array Json to replace it and append both current and new to the end of the Array Json   
	 * @applyFor Object
	 */
	public void appendKeyValue(String key, Long value){
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			appendKeyJsonValue(key, json);
		}else{
			throw new UnsupportedOperationException("non object json doesn't support appendKeyValue method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of Double
	 * <br/> If the key property doesn't exist in the inner map, just put them.
	 * <br/> If the key property already exists in the inner map, if the exist one is an Array Json, just append it to the end of it, If not an Array Json, use new Array Json to replace it and append both current and new to the end of the Array Json   
	 * @applyFor Object
	 */
	public void appendKeyValue(String key, Double value){
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			appendKeyJsonValue(key, json);
		}else{
			throw new UnsupportedOperationException("non object json doesn't support appendKeyValue method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of String
	 * <br/> If the key property doesn't exist in the inner map, just put them.
	 * <br/> If the key property already exists in the inner map, if the exist one is an Array Json, just append it to the end of it, If not an Array Json, use new Array Json to replace it and append both current and new to the end of the Array Json   
	 * @applyFor Object
	 */
	public void appendKeyValue(String key, String value){
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			appendKeyJsonValue(key, json);
		}else{
			throw new UnsupportedOperationException("non object json doesn't support appendKeyValue method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Object Json instance calls this method, UnsupportedOperationException will be thrown out.
	 * <br/> Key must not be null, or else UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must not be null, or else do nothing.
	 * <br/> If the key property doesn't exist in the inner map, just put them.
	 * <br/> If the key property already exists in the inner map, if the exist one is an Array Json, just append it to the end of it, If not an Array Json, use new Array Json to replace it and append both current and new to the end of the Array Json   
	 * @applyFor Object
	 */
	public void appendKeyJsonValue(String key, Json json){
		if(json == null){
			return;
		}
		if(this.isObject()){
			if(key == null){
				throw new UnsupportedDataTypeException("Key is null");
			}
			Json mapValue = this.map.get(key);
			if(mapValue == null){
				this.map.put(key, json);
			}else{
				if(mapValue.isArray()){
					mapValue.appendArrayWithJson(json);
				}else{
					Json jsonArray = new Json();
					jsonArray.initArray();
					jsonArray.appendArrayWithJson(mapValue);
					jsonArray.appendArrayWithJson(json);
					this.map.put(key, jsonArray);
				}
			}
		}else{
			throw new UnsupportedOperationException("non object json doesn't support appendKeyValue method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Array Json instance calls this method, UnsupportedOperationException will be thrown out
	 * <br/> If value is null, UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of Integer
	 * @applyFor Array
	 */
	public void appendArray(Integer value){
		if(this.isArray()){
			if(value == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			appendArrayWithJson(json);
		}else{
			throw new UnsupportedOperationException("non array json doesn't support appendArray method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Array Json instance calls this method, UnsupportedOperationException will be thrown out
	 * <br/> If value is null, UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of Long
	 * @applyFor Array
	 */
	public void appendArray(Long value){
		if(this.isArray()){
			if(value == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			appendArrayWithJson(json);
		}else{
			throw new UnsupportedOperationException("non array json doesn't support appendArray method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Array Json instance calls this method, UnsupportedOperationException will be thrown out
	 * <br/> If value is null, UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of Double
	 * @applyFor Array
	 */
	public void appendArray(Double value){
		if(this.isArray()){
			if(value == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			appendArrayWithJson(json);
		}else{
			throw new UnsupportedOperationException("non array json doesn't support appendArray method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note Non-Array Json instance calls this method, UnsupportedOperationException will be thrown out
	 * <br/> If value is null, UnsupportedDataTypeException will be thrown out.
	 * <br/> Value must be an instance of String
	 * @applyFor Array
	 */
	public void appendArray(String value){
		if(this.isArray()){
			if(value == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			Json json = new Json();
			json.initSimpleValue(value);
			appendArrayWithJson(json);
		}else{
			throw new UnsupportedOperationException("non array json doesn't support appendArray method");
		}
	}
	
	/**
	 * @throws UnsupportedDataTypeException
	 * @throws UnsupportedOperationException
	 * @Note If json is null, UnsupportedDataTypeException will be thrown out.
	 * @applyFor Array
	 */
	public void appendArrayWithJson(Json json){
		if(this.isArray()){
			if(json == null){
				throw new UnsupportedDataTypeException("Unsupport NULL value");
			}
			this.array.add(json);
		}else{
			throw new UnsupportedOperationException("non array json doesn't support appendArray method");
		}
	}
	
	
	/**
	 * @Note If Json instance is not an SimpleValue Json, then null will be returned.
	 * @applyFor All
	 */
	public Object value(){
		if(this.isSimpleValueJson()){
			return this.getValue();
		}else{
			return null;
		}
	}
	
	/**
	 * @Note If Json instance doesn't represent Integer, then null will be returned.
	 * @applyFor All
	 */
	public Integer intValue(){
		if(this.isInt()){
			return (Integer)this.value();
		}else{
			return null;
		}
	}
	
	/**
	 * @Note If Json instance doesn't represent Long, then null will be returned.
	 * @applyFor All
	 */
	public Long longValue(){
		if(this.isLong()){
			return (Long)this.value();
		}else if(this.isInt()){
			Integer intValue = (Integer)this.value();
			if(intValue != null){
				return intValue.longValue();
			}else{
				return null;
			}
		}else{
			return null;
		}
	}
	
	/**
	 * @applyFor All
	 */
	/*public Float floatValue(){
		if(this.isFloat()){
			return (Float)this.value();
		}else{
			return null;
		}
	}*/
	
	/**
	 * @Note If Json instance doesn't represent Double, then null will be returned.
	 * @applyFor All
	 */
	public Double doubleValue(){
		if(this.isDouble()){
			return (Double)this.value();
		}else{
			return null;
		}
	}
	
	/*
	 * @applyFor All
	 */
	/*public BigDecimal bigdecimalValue(){
		if(this.isBigDecimal()){
			return (BigDecimal)this.value();
		}else{
			return null;
		}
	}*/
	
	/**
	 * @Note If Json instance doesn't represent String, then null be returned.
	 * @applyFor All
	 */
	public String stringValue(){
		if(this.isString()){
			return (String)this.value();
		}else{
			return null;
		}
	}
	
	/**
	 * @Note If Json instance doesn't represent Object, then null will be returned.
	 * @applyFor All
	 */
	public Json jsonValueOf(String property){
		if(property == null){
			return null;
		}
		if(this.isObject()){
			return this.map.get(property);
		}
		return null;
	}
	
	/**
	 * @Note The result can be one of Integer, Long, Double, String, and null.
	 * @applyFor All
	 */
	public Object valueOf(String property){
		Json json = jsonValueOf(property);
		if(json != null){
			return json.value();
		}else{
			return null;
		}
	}
	
	/**
	 * @Note The result can be one of Integer and null.
	 * @applyFor All
	 */
	public Integer intValueOf(String property){
		Json json = jsonValueOf(property);
		if(json != null){
			return json.intValue();
		}else{
			return null;
		}
	}
	
	/**
	 * @Note The result can be one of Long and null.
	 * @Note If the Json instance represents Integer, then relevant Long value will be returned.
	 * @applyFor All
	 */
	public Long longValueOf(String property){
		Json json = jsonValueOf(property);
		if(json != null){
			return json.longValue();
		}else{
			return null;
		}
	}
	
	/**
	 * @applyFor All
	 */
	/*public Float floatValueOf(String property){
		Json json = jsonValueOf(property);
		if(json != null){
			return json.floatValue();
		}else{
			return null;
		}
	}*/
	
	/**
	 *@Note The result can be one of Double and null.
	 *@applyFor All 
	 */
	public Double doubleValueOf(String property){
		Json json = jsonValueOf(property);
		if(json != null){
			return json.doubleValue();
		}else{
			return null;
		}
	}
	
	/*
	 * @applyFor All
	 */
	/*public BigDecimal bigdecimalValueOf(String property){
		Json json = jsonValueOf(property);
		if(json != null){
			return json.bigdecimalValue();
		}else{
			return null;
		}
	}*/
	
	/**
	 *@Note The result can be one of String and null.
	 *@applyFor All 
	 */
	public String stringValueOf(String property){
		Json json = jsonValueOf(property);
		if(json != null){
			return json.stringValue();
		}else{
			return null;
		}
	}
	
	/**
	 * @Note If the Json instance doesn't represent Array, null will be returned.
	 * <br/> If the index less then zero, null will be returned.
	 * @applyFor All
	 */
	public Json indexJsonOf(int index){
		if(index < 0){
			return null;
		}
		if(index < this.length()){
			return this.array.get(index);
		}else{
			return null;
		}
	}
	
	/**
	 * @Note If the Json instance doesn't represent Array, null will be returned.
	 * <br/> If the index less then zero, null will be returned.
	 * <br/> The returned object must be one of Integer, Long, Double, String and null.
	 * @applyFor All
	 */
	public Object indexValueOf(int index){
		Json json = this.indexJsonOf(index);
		if(json != null && json.isSimpleValueJson()){
			return json.value();
		}else{
			return null;
		}
	}
	
	/**
	 * @Note If the Json instance doesn't represent Array, null will be returned.
	 * <br/> If the index less then zero, null will be returned.
	 * <br/> The returned value must be one of Integer and null.
	 * @applyFor All
	 */
	public Integer indexIntegerOf(int index){
		Json json = this.indexJsonOf(index);
		if(json != null){
			return json.intValue();
		}else{
			return null;
		}
	}
	
	/**
	 * @Note If the Json instance doesn't represent Array, null will be returned.
	 * <br/> If the index less then zero, null will be returned.
	 * <br/> The returned value must be one of Long and null.
	 * <br/> If the concrete Json instance represents Integer, then the relevant Long value will be returned.
	 * @applyFor All
	 */
	public Long indexLongOf(int index){
		Json json = this.indexJsonOf(index);
		if(json != null){
			return json.longValue();
		}else{
			return null;
		}
	}
	
	/*
	 * @applyFor All
	 */
	/*public Float indexFloatOf(int index){
		Json json = this.indexJsonOf(index);
		if(json != null){
			return json.floatValue();
		}else{
			return null;
		}
	}*/
	
	/**
	 * @Note If the Json instance doesn't represent Array, null will be returned.
	 * <br/> If the index less then zero, null will be returned.
	 * <br/> The returned value must be one of Double and null.
	 * @applyFor All
	 */
	public Double indexDoubleOf(int index){
		Json json = this.indexJsonOf(index);
		if(json != null){
			return json.doubleValue();
		}else{
			return null;
		}
	}
	
	/*
	 * @applyFor All
	 */
	/*public BigDecimal indexBigdecimalOf(int index){
		Json json = this.indexJsonOf(index);
		if(json != null){
			return json.bigdecimalValue();
		}else{
			return null;
		}
	}*/
	
	/**
	 * @Note If the Json instance doesn't represent Array, null will be returned.
	 * <br/> If the index less then zero, null will be returned.
	 * <br/> The result must be one of the String and null.
	 * @applyFor All
	 */
	public String indexStringOf(int index){
		Json json = this.indexJsonOf(index);
		if(json != null){
			return json.stringValue();
		}else{
			return null;
		}
	}
	
	/**
	 * @applyFor All
	 * @Note This method is used to translate current json object into String representation
	 */
	public String asString(){
		if(this.isObject()){
			StringBuffer sb = new StringBuffer();
			int currentIndex = 0;
			sb.append("{");
			Set<Map.Entry<String, Json>> set = this.map.entrySet();
			for(Map.Entry<String, Json> item : set){
				if(currentIndex == 0){
					sb.append("\"");
					sb.append(item.getKey());
					sb.append("\"");
					sb.append(":");
					sb.append(item.getValue() != null ? item.getValue().asString() : "");
				}else{
					sb.append(",\"");
					sb.append(item.getKey());
					sb.append("\"");
					sb.append(":");
					sb.append(item.getValue() != null ? item.getValue().asString() : "");
				}
				currentIndex++;
			}
			sb.append("}");
			return sb.toString();
		}else if(this.isArray()){
			StringBuffer sb = new StringBuffer();
			int currentIndex = 0;
			sb.append("[");
			for(Json json : this.array){
				if(currentIndex == 0){
					sb.append(json.asString());
				}else{
					sb.append(",");
					sb.append(json.asString());
				}
				currentIndex++;
			}
			sb.append("]");
			return sb.toString();
			
		}else if(this.isSimpleValueJson()){
			StringBuffer sb = new StringBuffer();
			if(this.isString()){
				String value = this.stringValue();
				if(value != null){
					value = value.replaceAll("\"", "\\\\\"");
					value = value.replaceAll("\n", "\\\\n");
					value = value.replaceAll("\r", "\\\\r");
					value = value.replaceAll("\t", "\\\\t");
				}
				sb.append("\"");
				sb.append(value);
				sb.append("\"");
			}else{
				sb.append(this.getValue() != null ? this.getValue().toString() : "0");
			}
			return sb.toString();
		}else{
			return "";
		}
	}
	
	public static void main(String[] args){
		Json json = JsonUtils.createJsonObject();
		Json jsonArray = JsonUtils.createJsonArray();
		jsonArray.appendArray(1);
		jsonArray.appendArray(2);
		jsonArray.appendArray(3);
		jsonArray.appendArray(4);
		json.keyValue("kkey", 123);
		
		System.out.println(Integer.parseInt("0123"));
		System.out.println(Double.parseDouble("+89."));
		//json.keyValue("arrayKkey", jsonArray);
		json.keyJsonValue("arrayKkey", jsonArray);
		json.appendKeyValue("kkey", "another string");
		System.out.println(json.asString());
	}
}
